package net.arogarth.aws.android.lib;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import android.util.Log;

import net.arogarth.aws.android.lib.ext.Base64;
import net.arogarth.aws.android.lib.models.HostetZone;
import net.arogarth.aws.android.lib.models.HostetZoneRecordSet;
import net.arogarth.aws.android.lib.models.HostetZoneRecordSet.DnsType;

public class AmazonRoute53 extends AmazonAWS {
	public AmazonRoute53() {
		super();
		
		this.service = "route53";
		
		this.version = "2011-05-05";
	}
	
	private String getTimestamp() {
        Calendar cal = Calendar.getInstance();
        DateFormat dfm = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
        return dfm.format(cal.getTime());
	}
	
	protected Element execute() throws AwsRequestException {
		Element root = null;

		try {
			if(!this.version.equals(""))
				params.put("Version", this.version);
			
			String ts = getTimestamp();
			
			Mac hmc = Mac.getInstance("HmacSHA256");
			hmc.init(new SecretKeySpec(this.awsSecretKey.getBytes(), "HmacSHA256"));
			String signature = new String(Base64.encodeBytes(hmc.doFinal(ts.getBytes())));
			
			HttpClient hc = new DefaultHttpClient();
			HttpGet hg = new HttpGet(this.URL);
			
			
			String auth = "AWS3-HTTPS AWSAccessKeyId=";
			auth += this.awsAccessKey;
			auth += ",Algorithm=HmacSHA256,Signature="+signature;
			
			hg.addHeader("X-Amzn-Authorization", auth);
			hg.addHeader("x-amz-date", ts);

			HttpResponse response = hc.execute(hg);

			InputStream in = response.getEntity().getContent();

			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			
			StringBuilder sb = new StringBuilder();
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
			String line = null;
			while ((line = bufferedReader.readLine()) != null) {
				line = line.replace("><", ">\n<");
				Log.d("RESPONSE", line);
				sb.append(line);
			}
			in.close();
			
			StringReader reader = new StringReader( sb.toString() );
			Document dom = builder.parse( new InputSource( reader ) );

			root = dom.getDocumentElement();

			NodeList errors = root.getElementsByTagName("Error");

			for (int i = 0; i < errors.getLength(); ) {
				NodeList error = errors.item(i).getChildNodes();
				String code = "";
				String message = "";

				for (int j = 0; j < error.getLength(); j++) {
					Node n = error.item(j);
					if (n.getNodeName().equalsIgnoreCase("code"))
						code = n.getFirstChild().getNodeValue();
					else if (n.getNodeName().equalsIgnoreCase("message"))
						message = n.getFirstChild().getNodeValue();
				}
				throw new AwsRequestException(code, message);
			}
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}
		
		return root;
	}

	public List<HostetZone> listHostetZones() throws AwsRequestException {
		Boolean hasMore = true;
		
		String marker = "";
		String url = "https://" + this.service + ".amazonaws.com/" + this.version + "/hostedzone?maxitems=10"; 
		this.URL = url;
		
		List<HostetZone> hostetZones = new ArrayList<HostetZone>();
		
		while (hasMore) {
			Element root = this.execute();
			
			XPath xpath = XPathFactory.newInstance().newXPath();
	
			NodeList items;
			
			try {
				
				NodeList markers = root.getElementsByTagName("NextMarker");
				if( markers.getLength() > 0 ) {
					hasMore = true;
					marker = markers.item(0).getTextContent();
				} else {
					hasMore = false;
					marker = "";
				}
				
				items = (NodeList) xpath.evaluate("//HostedZones/*",
						root, XPathConstants.NODESET);
	
				for (int i = 0; i < items.getLength(); i++) {
					HostetZone zone = new HostetZone();
					Node item = items.item(i);
					NodeList properties = item.getChildNodes();
	
					for (int j = 0; j < properties.getLength(); j++) {
						Node property = properties.item(j);
						String name = property.getNodeName();
	
						if (property.getFirstChild() == null)
							continue;
						
						String value = property.getFirstChild().getNodeValue();
	
						if (name.equalsIgnoreCase("Id")) {
							zone.setId(value.replace("/hostedzone/", ""));
						} else if (name.equalsIgnoreCase("Name")) {
							zone.setName(value);
						} else if (name.equalsIgnoreCase("CallerReference")) {
						}
					}
					
					hostetZones.add(zone);
					
				}
				
			} catch (XPathExpressionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			// Set marker for next call
			this.URL = url + "&marker="+marker;
		}
			
		return hostetZones;
	}
	
	/**
	 * @see http://docs.amazonwebservices.com/Route53/latest/DeveloperGuide/index.html?GetInfoAboutHostedZone.html
	 * @param hZoneId
	 * @return
	 * @throws AwsRequestException
	 */
	public List<HostetZoneRecordSet> listHostetZoneRecordSets(String hZoneId) throws AwsRequestException {
		this.URL = "https://" + this.service + ".amazonaws.com/" + this.version + "/hostedzone/" + hZoneId + "/rrset";

		List<HostetZoneRecordSet> hostetZones = new ArrayList<HostetZoneRecordSet>();
		Element root = this.execute();
		
		XPath xpath = XPathFactory.newInstance().newXPath();

		NodeList items;
		try {
			items = (NodeList) xpath.evaluate("//ResourceRecordSets/*",
					root, XPathConstants.NODESET);

			for (int i = 0; i < items.getLength(); i++) {
				HostetZoneRecordSet zone = new HostetZoneRecordSet();
				Node item = items.item(i);
				NodeList properties = item.getChildNodes();
	
				for (int j = 0; j < properties.getLength(); j++) {
					Node property = properties.item(j);
					String name = property.getNodeName();
	
					if (property.getFirstChild() == null)
						continue;
					
					if (name.equalsIgnoreCase("Name")) {
						zone.setName(property.getTextContent());
					} else if (name.equalsIgnoreCase("Type")) {
						DnsType type = Enum.valueOf(DnsType.class, property.getTextContent());
						zone.setType(type);
					} else if (name.equalsIgnoreCase("TTL")) {
						zone.setTtl(property.getTextContent());
					} else if (name.equalsIgnoreCase("SetIdentifier")) {
						zone.setSetIdentifier(property.getTextContent());
					} else if (name.equalsIgnoreCase("Weight")) {
						zone.setWeight(property.getTextContent());
					} else if (name.equalsIgnoreCase("ResourceRecords")) {
						
						NodeList targets = ((Element) property).getElementsByTagName("Value");
						for(int k = 0; k < targets.getLength(); k++) {
							Node n = targets.item(k);
							zone.getResourceRecords().add( n.getTextContent() );
						}
						
					} else if (name.equalsIgnoreCase("AliasTarget")) {
						
						NodeList targets = property.getChildNodes();
						for(int k = 0; k < targets.getLength(); k++) {
							Node n = targets.item(k);
							if( n.getNodeName().equalsIgnoreCase("DNSName") ) {
								zone.setDnsName(n.getTextContent());
							} else if( n.getNodeName().equalsIgnoreCase("HostedZoneId") ) {
								zone.setHostedZoneId(n.getTextContent());
							}
						}
						
					}
				}
				
				hostetZones.add(zone);
				
			}
		} catch (XPathExpressionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			
		return hostetZones;
	}
}
